package storage

import (
	"github.com/boltdb/bolt"
	"github.com/fagongzi/log"
	"os"
	"sync"
)

type BoltCfg struct {
	DataPath string
	FileMode os.FileMode
	Options  bolt.Options
}

type boltDriver struct {
	db         *bolt.DB
	metaEngine Engine
	dataEngine DataEngine
	kvEngine   KVEngine
	hashEngine HashEngine
	listEngine ListEngine
	setEngine  SetEngine
	zsetEngine ZSetEngine
}

type boltWriteBatch struct {
	sync.Mutex
	opts []*opt
}

func newBoltWriteBatch() WriteBatch {
	return &boltWriteBatch{}
}

func (wb *boltWriteBatch) Set(key []byte, value []byte) error {
	wb.Lock()
	defer wb.Unlock()

	wb.opts = append(wb.opts, &opt{
		key:   key,
		value: value,
	})

	return nil
}

func (wb *boltWriteBatch) Delete(key []byte) error {
	wb.Lock()
	defer wb.Unlock()

	wb.opts = append(wb.opts, &opt{
		key:      key,
		isDelete: true,
	})

	return nil
}

func NewBoltDriver(cfg *BoltCfg) (Driver, error) {
	var opts *bolt.Options
	opts = bolt.DefaultOptions

	db, err := bolt.Open(cfg.DataPath, cfg.FileMode, opts)
	if err != nil {
		log.Fatal(err)
	}

	db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte("defaultBucket"))
		if err != nil {
			return err
		}
		return nil
	})

	driver := &boltDriver{
		db: db,
	}
	driver.init(cfg)
	return driver, nil
}

func (b *boltDriver) init(cfg *BoltCfg) {

}

func (b *boltDriver) GetEngine() Engine {
	return b.metaEngine
}

func (b *boltDriver) GetDataEngine() DataEngine {
	return b.dataEngine
}

func (b *boltDriver) GetKVEngine() KVEngine {
	return b.kvEngine
}

func (b *boltDriver) GetHashEngine() HashEngine {
	return b.hashEngine
}

func (b *boltDriver) GetSetEngine() SetEngine {
	return b.setEngine
}

func (b *boltDriver) GetZSetEngine() ZSetEngine {
	return b.zsetEngine
}

func (b *boltDriver) GetListEngine() ListEngine {
	return b.listEngine
}

func (b *boltDriver) NewWriteBatch() WriteBatch {
	return newBoltWriteBatch()
}

func (b *boltDriver) Write(wb WriteBatch, sync bool) error {
	return b.db.Batch(func(tx *bolt.Tx) error {
		var err error
		bucket := tx.Bucket([]byte("defaultBucket"))
		for _, opt := range wb.(*boltWriteBatch).opts {
			curKey, curVal := opt.key, opt.value
			if opt.isDelete {
				err = bucket.Delete(curKey)
			} else {
				err = bucket.Put(curKey, curVal)
			}
			if err != nil {
				return err
			}
		}
		return nil
	})
}
